#include "cpu/cpu.h"
#include "cpu/irq.h"
#include "os_cfg.h"
#include "comm/cpu_instr.h"
#include "ipc/mutex.h"
#include "core/syscall.h"


static segment_desc_t gdt_table[GDT_TABLE_SIZE];
static mutex_t mutex;


void segment_desc_set(uint32_t selector, uint32_t base, uint32_t limit, uint16_t attr) {
    segment_desc_t *desc = gdt_table + (selector >> 3);
    
    if (limit > 0xFFFFF) {
        attr |= 0x8000;
        limit /= 0x1000;
    }
    
    desc->limit15_0 = limit & 0xFFFF;
    desc->base15_0 = base & 0xFFFF;
    desc->base23_16 = (base >> 16) & 0xFF;
    desc->attr = attr | (((limit >> 16) & 0xF) << 8);
    desc->base31_24 = (base >> 24) & 0xFF;
}

void init_gdt(void) {
    for (int i = 0; i < GDT_TABLE_SIZE; i ++) {
        segment_desc_set(i * sizeof(segment_desc_t), 0, 0, 0);
    }
    // 重新设置系统代码段和数据段
    segment_desc_set(KERNEL_SELECTOR_CS, 0, 0xFFFFFFFF,
        SEG_P | SEG_DPL0 | SEG_S_NORMAL | SEG_TYPE_CODE 
        | SEG_TYPE_CODE_RA | SEG_D);
    segment_desc_set(KERNEL_SELECTOR_DS, 0, 0xFFFFFFFF,
        SEG_P | SEG_DPL0 | SEG_S_NORMAL | SEG_TYPE_DATA 
        | SEG_TYPE_DATA_RW | SEG_D);

    gate_desc_set((gate_desc_t *)(gdt_table + (SELECTOR_SYSCALL >> 3)),
        KERNEL_SELECTOR_CS, (uint32_t)exception_handler_syscall,
        GATE_P | GATE_DPL3 | GATE_TYPE_SYSCALL | SYSCALL_PARM_COUNT
    );


    lgdt((uint32_t)gdt_table, sizeof(gdt_table));
}

void cpu_init(void) {
    mutex_init(&mutex);
    init_gdt();
}

void gate_desc_set(gate_desc_t *desc, uint16_t selector, uint32_t offset, uint16_t attr) {
    desc->offset15_0 = offset  & 0xFFFF;
    desc->selector = selector;
    desc->attr = attr;
    desc->offset32_16 = offset >> 16;
}

int gdt_alloc_desc() {
    mutex_lock(&mutex);
    for (int i = 1; i < GDT_TABLE_SIZE; i ++) {
        segment_desc_t *desc = gdt_table + i;
        if (desc->attr == 0) {
            mutex_unlock(&mutex);
            return i * sizeof(segment_desc_t);
        }
    }
    mutex_unlock(&mutex);
    return -1;
}

void gdt_free_sel(int sel) {
    mutex_lock(&mutex);
    gdt_table[sel/sizeof(segment_desc_t)].attr = 0;
    mutex_unlock(&mutex);
}


void  switch_to_tss (uint32_t tss_sel) {
    far_jump(tss_sel, 0);
}